package own.stu.jobgib.playown.fsm.easyStates.demo;

import lombok.extern.slf4j.Slf4j;
import own.stu.jobgib.playown.fsm.easyStates.api.*;
import own.stu.jobgib.playown.fsm.easyStates.core.FiniteStateMachineBuilder;
import own.stu.jobgib.playown.fsm.easyStates.core.TransitionBuilder;

import java.util.Date;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

@Slf4j
public class turnstile {

    public static void main(String[] args) throws FiniteStateMachineException {
        /*
         * Define FSM states
         */
        State locked = new State("locked");
        State unLocked = new State("unlocked");

        Set<State> states = new HashSet<>();
        states.add(locked);
        states.add(unLocked);

        /*
         * Define FSM transitions
         */
        Transition unlock = new TransitionBuilder()
                .name("unlock").sourceState(locked).eventType(CoinEvent.class).eventHandler(new Unlock()).targetState(unLocked)
                .builder();
        Transition pushLocked = new TransitionBuilder()
                .name("pushLocked").sourceState(locked).eventType(PushEvent.class).targetState(locked)
                .builder();
        Transition lock = new TransitionBuilder()
                .name("lock").sourceState(unLocked).eventType(PushEvent.class).eventHandler(new Lock()).targetState(locked)
                .builder();
        Transition coinUnlocked = new TransitionBuilder()
                .name("coinUnlocked").sourceState(unLocked).eventType(CoinEvent.class).targetState(unLocked)
                .builder();

        /*
         * Build FSM instance
         */
        FiniteStateMachine turnstileStateMachine = new FiniteStateMachineBuilder(states, locked)
                .registerTransition(lock)
                .registerTransition(pushLocked)
                .registerTransition(unlock)
                .registerTransition(coinUnlocked)
                .build();

        /*
         * Fire some events and print FSM state
         */
        System.out.println("Turnstile initial state : " + turnstileStateMachine.getCurrentState().getName());

        Scanner scanner = new Scanner(System.in);
        System.out.println("Which event do you want to fire?");
        System.out.println("1. Push [p]");
        System.out.println("2. Coin [c]");
        System.out.println("Press [q] to quit tutorial.");
        while (true) {
            String input = scanner.nextLine();
            if (input.trim().equalsIgnoreCase("p")) {
                System.out.println("input = " + input.trim());
                System.out.println("Firing push event..");
                turnstileStateMachine.fire(new PushEvent());
                System.out.println("Turnstile state : " + turnstileStateMachine.getCurrentState().getName());
            }
            if (input.trim().equalsIgnoreCase("c")) {
                System.out.println("input = " + input.trim());
                System.out.println("Firing coin event..");
                turnstileStateMachine.fire(new CoinEvent());
                System.out.println("Turnstile state : " + turnstileStateMachine.getCurrentState().getName());
            }
            if (input.trim().equalsIgnoreCase("q")) {
                System.out.println("input = " + input.trim());
                System.out.println("Bye!");
                System.exit(0);
            }
        }
    }

    // ========== event ============
    static class CoinEvent extends AbstractEvent {
        public CoinEvent() {
            super("CoinEvent");
        }
    }

    static class PushEvent extends AbstractEvent {
        public PushEvent() {
            super("PushEvent");
        }
    }

    // ========== eventHandler ============
    static class Lock implements EventHandler<PushEvent> {

        @Override
        public void handle(PushEvent event) throws Exception {
            log.info("Notified about event '{}' triggered at {}", event.getName(), new Date(event.getTimestamp()));
            log.info("Locking turnstile..");
        }
    }

    static class Unlock implements EventHandler<CoinEvent> {

        public void handle(CoinEvent event) {
            log.info("Notified about event '{}' triggered at {}", event.getName(), new Date(event.getTimestamp()));
            log.info("Unlocking turnstile..");
        }
    }
}
